﻿




using System.Linq;
using System.Collections.Generic;
using System;
 



using System.IO;

namespace LinqToFileSystem
{
	/// <summary>
    /// Defines an interface that must be implemented to generate the LinqToTree methods
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ILinqTree<T>
    {
        IEnumerable<T> Children();

        T Parent { get; }
    }
  
    public static class TreeExtensions
    {
        /// <summary>
        /// Returns a collection of descendant elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> Descendants(this DirectoryInfo item)
        {
            ILinqTree<DirectoryInfo> adapter = new FileSystemTreeAdapter(item);
            foreach (var child in adapter.Children())
            {
                yield return child;

                foreach (var grandChild in child.Descendants())
                {
                    yield return grandChild;
                }
            }
        }    
           
        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> DescendantsAndSelf(this DirectoryInfo item)
        {            
            yield return item;

            foreach (var child in item.Descendants())
            {
                yield return child;
            }
        }
        
        /// <summary>
        /// Returns a collection of ancestor elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> Ancestors(this DirectoryInfo item)
        {
            ILinqTree<DirectoryInfo> adapter = new FileSystemTreeAdapter(item);
            
            var parent = adapter.Parent;
            while(parent != null)
            {
                yield return parent;
                adapter = new FileSystemTreeAdapter(parent);
                parent = adapter.Parent;
            }
        } 
        
        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> AncestorsAndSelf(this DirectoryInfo item)
        {
            yield return item;

            foreach (var ancestor in item.Ancestors())
            {
                yield return ancestor;
            }
        }
        
        /// <summary>
        /// Returns a collection of child elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> Elements(this DirectoryInfo item)
        {
            ILinqTree<DirectoryInfo> adapter = new FileSystemTreeAdapter(item);
            foreach (var child in adapter.Children())
            {
                yield return child;                
            }
        }
        
        /// <summary>
        /// Returns a collection of the sibling elements before this node, in document order.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> ElementsBeforeSelf(this DirectoryInfo item)
        {
			if (item.Ancestors().FirstOrDefault()==null)
				yield break;
            foreach (var child in item.Ancestors().First().Elements())
            {
				if (child.FullName == item.FullName)
					break;
                yield return child;                
            }
        }
        
        /// <summary>
        /// Returns a collection of the elements after this node, in document order.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> ElementsAfterSelf(this DirectoryInfo item)
        {
			if (item.Ancestors().FirstOrDefault()==null)
				yield break;
            bool afterSelf = false;
            foreach (var child in item.Ancestors().First().Elements())
            {
				if (afterSelf)
					yield return child;                
                
                if (child.FullName == item.FullName)
					afterSelf=true;
            }
        }
        
        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> ElementsAndSelf(this DirectoryInfo item)
        {
            yield return item;

            foreach (var child in item.Elements())
            {
                yield return child;
            }
        }
    }
    
    public static class EnumerableTreeExtensions
    {
		/// <summary>
        /// Applies the given function to each of the items in the supplied
        /// IEnumerable.
        /// </summary>
        private static IEnumerable<DirectoryInfo> DrillDown(this IEnumerable<DirectoryInfo> items,
            Func<DirectoryInfo, IEnumerable<DirectoryInfo>> function)
        {
            foreach(var item in items)
            {
                foreach(var itemChild in function(item))
                {
                    yield return itemChild;
                }
            }
        }

    
        /// <summary>
        /// Returns a collection of descendant elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> Descendants(this IEnumerable<DirectoryInfo> items)
        {
            return items.DrillDown(i => i.Descendants());
        }    
           
        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> DescendantsAndSelf(this IEnumerable<DirectoryInfo> items)
        {            
            return items.DrillDown(i => i.DescendantsAndSelf());
        }
        
        /// <summary>
        /// Returns a collection of ancestor elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> Ancestors(this IEnumerable<DirectoryInfo> items)
        {
            return items.DrillDown(i => i.Ancestors());
        } 
        
        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> AncestorsAndSelf(this IEnumerable<DirectoryInfo> items)
        {
            return items.DrillDown(i => i.AncestorsAndSelf());
        }
        
        /// <summary>
        /// Returns a collection of child elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> Elements(this IEnumerable<DirectoryInfo> items)
        {
            return items.DrillDown(i => i.Elements());
        }
        
        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// </summary>
	    public static IEnumerable<DirectoryInfo> ElementsAndSelf(this IEnumerable<DirectoryInfo> items)
        {
            return items.DrillDown(i => i.ElementsAndSelf());
        }

    }
}